{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "from IPython.display import display              # 美化输出数组\n",
    "InteractiveShell.ast_node_interactivity = 'all'  # 打印所有单行变量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### python数据存储原理\n",
    "\n",
    "**变量 引用 对象**\n",
    "\n",
    "    x = 1\n",
    "\n",
    "内存中只创建了对象`1`，变量x只是贴在对象`1`的标签，`=`是赋值即引用  \n",
    "在python中一切皆对象，里面又有不可变对象和可变对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 5  # 内存中创建了一个对象5\n",
    "b = 5\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "94251520061376"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "94251520061376"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(a)\n",
    "id(b)  # 一个对象贴了两个标签"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "94251520061312"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = 3  # 内存中新建了一个对象3\n",
    "id(b)  # 对象引用换成了b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上例 a, b 共享了同一个 ID、同一个值、同一个类型。因此 a, b 表达的是同一个对象，但 a, b 又明显是不同的，比如一个叫 'a'，一个叫 'b'...既然是同一个对象，为什么又有不同的名字呢？难道名字不是对象的属性？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 标识符\n",
    "事实确实如此，这是 Python 比较特殊一点：如同'a' 'b' 这样的名称其实有一个共同的名字：identifier（注意不要与 ID 混淆了），中文名为“标识符”，来解释一下：\n",
    "\n",
    "标识符：各类对象的名称，比如函数名、方法名、类名，变量名、常量名等。\n",
    "\n",
    "在 Python 中赋值并不会直接复制数据，而只是将名称绑定到对象，对象本身是不知道也不需要关心（该关心这个的是程序猿）自己叫什么名字的。一个对象甚至可以指向不同的标识符，上例中的'a' 'b'便是如此。真正管理这些名子的事物叫“命名空间”。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 命名空间\n",
    "命名空间（Namespace）：名字（标识符）到对象的映射。\n",
    "\n",
    "简而言之，命名空间可以理解为：记录对象和对象名字对应关系的空间；现今 Python 的大部分命名空间是通过字典来实现的，也即一个命名空间就是名字到对象的映射，标识符是键，对象则是值。\n",
    "\n",
    "### 作用域\n",
    "与命名空间相对的一个概念就是“作用域”，那么什么又是作用域呢？\n",
    "\n",
    "作用域（Scope）：本质是一块文本区域，Python 通过该文本区域可以直接访问相应的命名空间。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LEGB 命名空间\n",
    "\n",
    "这四类命名空间可以简记为 LEGB:\n",
    "\n",
    "局部命名空间（local）：指的是一个函数或者一个类所定义的名称空间；包括函数的参数、局部变量、类的属性等。\n",
    "\n",
    "闭包命名空间（enclosing function）：闭包函数 的名称空间（Python 3 引入）。\n",
    "\n",
    "全局命名空间（global）：读入一个模块（也即一个.py文档）后产生的名称空间。\n",
    "\n",
    "内建命名空间（builtin）：Python 解释器启动时自动载入__built__模块后所形成的名称空间；诸如 str/list/dict...等内置对象的名称就处于这里。\n",
    "\n",
    "```\n",
    "scopes = {\n",
    "    \"local\": {\"locals\": None,\n",
    "             \"non-local\": {\"locals\": None,\n",
    "                          \"global\": {\"locals\": None,\n",
    "                                    \"built-in\": [\"built-ins\"]}}},\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### LEGB 访问规则\n",
    "同样的标识符在各层命名空间中可以被重复使用而不会发生冲突，但 Python 寻找一个标识符的过程总是从当前层开始逐层往上找，直到首次找到这个标识符为止："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### global、nonlocal、locals()、globals()\n",
    "\n",
    "* global：声明该标识符引用的对象来自于全局变量，但并不能直接在当前作用域创建该标识符\n",
    "* nonlocal：声明该标识符引用的对象来自于父函数变量，并在当前作用域创建与父函数变量同名的标识符\n",
    "* locals(): 返回是当前局部变量的深拷贝，修改locals() 中变量值的时候，实际上对于原变量本身是没有任何影响的。  \n",
    "* globals(): 返回的是全局变量的字典，修改其中的内容，值会真正的发生改变"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'do_global': <function __main__.func.<locals>.do_global>,\n",
       " 'do_local': <function __main__.func.<locals>.do_local>,\n",
       " 'do_nonlocal': <function __main__.func.<locals>.do_nonlocal>,\n",
       " 'spam': 'func_scope'}"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "do_local_scope: {'spam': 'local spam'}\n",
      "do_nonlocal_scope: {'spam': 'func_scope', 'b': 1}\n",
      "do_global_scope: {}\n"
     ]
    }
   ],
   "source": [
    "def func():\n",
    "    def do_local():\n",
    "        spam = \"local spam\"\n",
    "        print('do_local_scope:', locals())\n",
    "    def do_nonlocal():\n",
    "        nonlocal spam\n",
    "        print('do_nonlocal_scope:', locals())\n",
    "        spam = \"nonlocal spam\"\n",
    "    def do_global():\n",
    "        global spam\n",
    "        print('do_global_scope:', locals())\n",
    "        spam = \"global spam\"\n",
    "        \n",
    "    spam = \"func_scope\"\n",
    "    display(locals())\n",
    "    do_local()\n",
    "    do_nonlocal()\n",
    "    do_global()\n",
    "\n",
    "func()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "After local assignment: test spam\n",
      "After nonlocal assignment: nonlocal spam\n",
      "After global assignment: nonlocal spam\n",
      "In global scope: global spam\n"
     ]
    }
   ],
   "source": [
    "def func():\n",
    "    def do_local():\n",
    "        spam = \"local spam\"\n",
    "    def do_nonlocal():\n",
    "        nonlocal spam\n",
    "        spam = \"nonlocal spam\"\n",
    "    def do_global():\n",
    "        global spam\n",
    "        spam = \"global spam\"\n",
    "        \n",
    "    spam = \"test spam\"\n",
    "    do_local()\n",
    "    print(\"After local assignment:\", spam)\n",
    "    \n",
    "    do_nonlocal()\n",
    "    print(\"After nonlocal assignment:\", spam)\n",
    "    \n",
    "    do_global()\n",
    "    print(\"After global assignment:\", spam)\n",
    "\n",
    "func()\n",
    "print(\"In global scope:\", spam)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 理解a = a + 1\n",
    "如果在现实中数学去理解，肯定会很困惑。但是在python中，或者是面向对象的编程语言中，一切就说的通，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 1\n",
    "a = a + 1  # 上→中→右→左\n",
    "\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 上\n",
    "上就是`a = 1`，该赋值发生在全局作用域，a标志符和1在内存中的对象建立联系"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 中\n",
    "中就是`=`这个赋值操作发生在哪，也是在全局作用域，`这一步很关键`，确定了后面的操作是在哪进行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 右\n",
    "因为在上一步`中`里确定了是全局作用域，那么`a + 1`就会去全局作用域中找a对应的对象（1在内存中对应的对象）参与计算，结果是2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 左\n",
    "这一步要做什么是由`中`这一步决定的，这里是赋值。左边的标志符（a）和上一步`右`的结果（2在内存中对应的对象）建立联系"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 可变对象的引用(除了数字和字符串)\n",
    "> 可以创建多个\"一样\"的对象，但是id不同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "140201480827080"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "[1, 2, 3]"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "140201480830088"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "140201480813512"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = [1, 2, 3]\n",
    "id(x)  # -->\n",
    "\n",
    "y = x[:]\n",
    "y      # -->\n",
    "id(y)  # -->\n",
    "\n",
    "z = [1, 2, 3]\n",
    "id(z)  # -->"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 解析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(140201480903624, 140201480812360)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 'a'\n",
    "y = ['a', 1, ['a', 1]]\n",
    "\n",
    "id(y), id(y[2])  # 列表对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(140202005359616, 140202005359616, 140202005359616)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(x), id(y[0]), id(y[2][0])  # 三个 'a' id一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(94251520061248, 94251520061248)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(y[1]), id(y[2][1])  # 列表的子对象数字1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['a', 1]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "140201480903624"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 修改列表内容\n",
    "y.remove(['a', 1])\n",
    "\n",
    "y     # -->\n",
    "id(y) #--> 因为是修改原对象，所有id不变"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 可变对象的浅拷贝"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': [1, 2, 3]}"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = {'a':1, 'b':[1, 2, 3]}  # 最底层的对象只有1,2,3\n",
    "y = x.copy()\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(140201480813832, 140201480813256)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(x),id(y)  # 父对象id显示不一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(94251520061248, 94251520061248)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(x['a']), id(y['a'])  # 子对象'a'的id一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(140201480929928, 140201480929928)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(x['b']), id(y['b'])  # 子对象'b'的id一样"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面可以看出，浅复制只是创建一个新的父对象，子对象还是同一个对象\n",
    "\n",
    "#### 修改字典的列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': [1, 3]}"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': [1, 3]}"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = {'a':1, 'b':[1, 2, 3]}\n",
    "y = x.copy()\n",
    "y['b'].remove(2)\n",
    "\n",
    "y  # -->\n",
    "x  # -->"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 导入copy模块实现deep copy\n",
    "\n",
    "> deepcopy可以创建两个id不一样的可变对象，但不影响不可变对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "140592905522888"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "140592905541640"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import copy\n",
    "x = {'a':1, 'b':[1, 2, 3]}\n",
    "y = copy.copy(x)                # 浅拷贝和前面一样\n",
    "z = copy.deepcopy(x)            # 深拷贝\n",
    "\n",
    "id(x['b'])  # -->\n",
    "id(z['b'])  # -->"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': [1, 2, 3]}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': [1, 3]}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z['b'].remove(2)\n",
    "\n",
    "x  # -->\n",
    "z  # -->"
   ]
  }
 ],
 "metadata": {
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4rc1"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "toc_cell": false,
   "toc_position": {
    "height": "839px",
    "left": "0px",
    "right": "1651px",
    "top": "107px",
    "width": "269px"
   },
   "toc_section_display": "block",
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": "15",
    "lenType": "10",
    "lenVar": "60"
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
